// *************************************************************************************************
//
// MK3D Engine
// --------------------------------------
// Copyright (C) 2007-2011 Zeb (mailto: zebbey@gmail.com)
//
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//
// *************************************************************************************************

#include "mk_header.h"
#include "mk_error.h"
#include "mk_engine.h"
#include "mk_d3d9_render_device.h"
#include "mk_d3d9_hardware_buffer.h"
#include "mk_d3d9_render_input.h"
#include "mk_d3d9_texture.h"
#include "mk_d3d9_shader.h"
#include "mk_d3d9_render_target.h"
#include "mk_profiler.h"
#include "mk_d3d9_utilities.h"
#include "mk_render_window.h"

namespace MK3DE
{
	CD3D9RenderDevice::CD3D9RenderDevice()
	{
		ZeroMemory(&_d3dpp, sizeof(D3DPRESENT_PARAMETERS));
		ZeroMemory(&_d3dcaps, sizeof(D3DCAPS9));

		_pd3d9 = NULL;
		_pd3d9device = NULL; 
		_bStencilBuffer = FALSE;
		_useIndexBuffer = FALSE;
		_prevRenderTarget = NULL;
		_prevDSSurface = NULL;
	}

	CD3D9RenderDevice::~CD3D9RenderDevice()
	{
		SAFE_RELEASE(_prevDSSurface);
		SAFE_RELEASE(_prevRenderTarget);
		SAFE_RELEASE(_pd3d9device);
		SAFE_RELEASE(_pd3d9);   
	}

	BOOL CD3D9RenderDevice::Create(void* winHandle)
	{
		HRESULT hr = E_FAIL;
		BOOL ret = FALSE;

		_pd3d9 = Direct3DCreate9(D3D_SDK_VERSION);
		MK_PROCESS_ERROR(_pd3d9);

		ZeroMemory(&_d3dpp, sizeof(D3DPRESENT_PARAMETERS));
		ZeroMemory(&_d3dcaps, sizeof(D3DCAPS9));

		DWORD Width = _s_options[RDO_WIDTH];
		DWORD Height = _s_options[RDO_HEIGHT];
		if (Width == 0 || Height == 0)
		{
			RECT rc;
			GetClientRect(GetDesktopWindow(), &rc);
			Width = rc.right - rc.left;
			Height = rc.bottom - rc.top;
		}

		//_d3dpp.SwapEffect				    = _s_options[RDO_FULLSCREEN] ? D3DSWAPEFFECT_DISCARD : D3DSWAPEFFECT_COPY;
		_d3dpp.SwapEffect				    = D3DSWAPEFFECT_DISCARD;
		_d3dpp.BackBufferWidth              = Width;
		_d3dpp.BackBufferHeight             = Height;
		_d3dpp.Windowed					    = _s_options[RDO_FULLSCREEN] ? FALSE : TRUE;
		_d3dpp.hDeviceWindow				= (HWND)winHandle;
		_d3dpp.BackBufferCount				= 1;
		_d3dpp.FullScreen_RefreshRateInHz	= D3DPRESENT_RATE_DEFAULT;
		_d3dpp.PresentationInterval		    = D3DPRESENT_INTERVAL_IMMEDIATE;
		_d3dpp.MultiSampleType				= D3DMULTISAMPLE_NONE;
		_d3dpp.MultiSampleQuality			= 0;
		_d3dpp.BackBufferFormat             = _s_options[RDO_COLOR_BIT] == 32 ? D3DFMT_X8R8G8B8 : D3DFMT_R5G6B5;

		ret = __CheckDepthStencilBufferFormat();
		MK_PROCESS_ERROR(ret);

		ret = __CreateDevice((HWND)winHandle);
		MK_PROCESS_ERROR(ret);

		hr = _pd3d9device->GetDeviceCaps(&_d3dcaps);
		MK_PROCESS_COM_ERROR(hr);

		RECT rc;
		GetClientRect((HWND)winHandle, &rc);
		ret = SetViewport(0, 0, (DWORD)rc.right - rc.left, (DWORD)rc.bottom - rc.top, 0, 1.0f);
		MK_PROCESS_ERROR(ret);

		__SetDefaultRenderState();

		hr = S_OK;
Exit0:
		if (FAILED(hr))
		{
			SAFE_RELEASE(_pd3d9device);
			SAFE_RELEASE(_pd3d9);
		}

		return hr == S_OK ? TRUE : FALSE;
	}

	void CD3D9RenderDevice::GetBackBufferDimension(DWORD& width, DWORD& height)
	{
		width = (DWORD)_d3dpp.BackBufferWidth;
		height = (DWORD)_d3dpp.BackBufferHeight;
	}

	PIXEL_FORMAT CD3D9RenderDevice::GetBackBufferFormat()
	{
		return FormatMapping(_d3dpp.BackBufferFormat);
	}

	BOOL CD3D9RenderDevice::__CreateDevice(HWND hWnd)
	{
		if (SUCCEEDED(_pd3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
			D3DCREATE_MIXED_VERTEXPROCESSING, &_d3dpp, &_pd3d9device)))
		{
			GetEngine()->GetLog()->Write(MKLT_INFO, "Create mixed vertex processing Direct3D device ok.");
			return TRUE;
		}

		if (SUCCEEDED(_pd3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
			D3DCREATE_HARDWARE_VERTEXPROCESSING, &_d3dpp, &_pd3d9device)))
		{
			GetEngine()->GetLog()->Write(MKLT_INFO, "Create hardware vertex processing Direct3D device ok.");
			return TRUE;
		}

		if (SUCCEEDED(_pd3d9->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 
			D3DCREATE_SOFTWARE_VERTEXPROCESSING, &_d3dpp, &_pd3d9device)))
		{
			GetEngine()->GetLog()->Write(MKLT_INFO, "Create software vertex processing Direct3D device ok.");
			return TRUE;
		}

		return FALSE;
	}

	BOOL CD3D9RenderDevice::__CheckDepthStencilBufferFormat()
	{
		if (SUCCEEDED(_pd3d9->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 
			_d3dpp.BackBufferFormat, _d3dpp.BackBufferFormat, D3DFMT_D24S8)))
		{
			GetEngine()->GetLog()->Write(MKLT_INFO, "Choose depth-stencil buffer format: D3DFMT_D24S8.");
			_d3dpp.AutoDepthStencilFormat = D3DFMT_D24S8;
			_bStencilBuffer = TRUE;
		}
		else if (SUCCEEDED(_pd3d9->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 
			_d3dpp.BackBufferFormat, _d3dpp.BackBufferFormat, D3DFMT_D24X8)))
		{
			GetEngine()->GetLog()->Write(MKLT_INFO, "Choose depth-stencil buffer format: D3DFMT_D24X8.");
			_d3dpp.AutoDepthStencilFormat = D3DFMT_D24X8;
			_bStencilBuffer = FALSE;
		}
		else if (SUCCEEDED(_pd3d9->CheckDepthStencilMatch(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, 
			_d3dpp.BackBufferFormat, _d3dpp.BackBufferFormat, D3DFMT_D16)))
		{
			GetEngine()->GetLog()->Write(MKLT_INFO, "Choose depth-stencil buffer format: D3DFMT_D16.");
			_d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
			_bStencilBuffer = FALSE;
		}
		else
			return FALSE;

		_d3dpp.EnableAutoDepthStencil = TRUE; 
		return TRUE;
	}

	void CD3D9RenderDevice::__SetDefaultRenderState()
	{
		_pd3d9device->SetRenderState(D3DRS_LIGHTING, FALSE);
		_pd3d9device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
		_pd3d9device->SetRenderState(D3DRS_ZENABLE, TRUE);
		_pd3d9device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);

		_pd3d9device->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
		_pd3d9device->SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);

		_pd3d9device->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
		_pd3d9device->SetSamplerState(0, D3DSAMP_MINFILTER , D3DTEXF_LINEAR);
		_pd3d9device->SetSamplerState(0, D3DSAMP_MIPFILTER , D3DTEXF_LINEAR);

	}

	BOOL CD3D9RenderDevice::BeginFrame()
	{
		HRESULT hr = _pd3d9device->BeginScene();
		MK_PROCESS_COM_ERROR(hr);

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CD3D9RenderDevice::Clear(BOOL clearBackBuffer, BOOL clearDepth, BOOL clearStencil, DWORD backBufferColor, float depth, DWORD stencil)
	{
		if (clearBackBuffer || clearDepth || clearStencil)
		{
			DWORD flag = 0;
			if (clearBackBuffer)
				flag |= D3DCLEAR_TARGET;
			if (clearDepth)
				flag |= D3DCLEAR_ZBUFFER;
			if (clearStencil && _bStencilBuffer)
				flag |= D3DCLEAR_STENCIL;

			HRESULT hr = _pd3d9device->Clear(0, NULL, flag, backBufferColor, depth, stencil);
			MK_PROCESS_COM_ERROR(hr);
		}

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CD3D9RenderDevice::EndFrame()
	{
		HRESULT hr = _pd3d9device->EndScene();
		MK_PROCESS_COM_ERROR(hr);

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CD3D9RenderDevice::Present(CRenderWindow* pwin)
	{
		HWND hWnd = NULL;
		if (pwin)
			hWnd = (HWND)pwin->GetWindowHandle();

		HRESULT hr = E_FAIL;
		if (_d3dpp.Windowed)
		{
			RECT rc;
			rc.left = _viewport.X;
			rc.top = _viewport.Y;
			rc.right = rc.left + _viewport.Width;
			rc.bottom = rc.top + _viewport.Height;
			hr = _pd3d9device->Present(&rc, NULL, hWnd, NULL);
			MK_PROCESS_COM_ERROR(hr);
		}
		else
		{
			hr = _pd3d9device->Present(NULL, NULL, hWnd, NULL);
			MK_PROCESS_COM_ERROR(hr);
		}

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CD3D9RenderDevice::SetViewport(DWORD x, DWORD y, DWORD Width, DWORD Height, FLOAT minZ, 
		FLOAT maxZ)
	{
		BOOL ret = FALSE;
		_viewport.X = x;
		_viewport.Y = y;
		_viewport.Width = Width;
		_viewport.Height = Height;
		_viewport.MinZ = minZ;
		_viewport.MaxZ = maxZ;
		HRESULT hr = _pd3d9device->SetViewport(&_viewport);
		MK_PROCESS_COM_ERROR(hr);

		return TRUE;
Exit0:
		return FALSE;
	}

	void CD3D9RenderDevice::GetViewport(DWORD& x, DWORD& y, DWORD& Width, DWORD& Height, float& minZ, float& maxZ)
	{
		x = _viewport.X;
		y = _viewport.Y;
		Width = _viewport.Width;
		Height = _viewport.Height;
		minZ = _viewport.MinZ;
		maxZ = _viewport.MaxZ;
	}

	BOOL CD3D9RenderDevice::OnResize(CRenderWindow* pwin)
	{
		DWORD width = pwin->GetWidth();
		DWORD height = pwin->GetHeight();
		if (_d3dpp.BackBufferWidth < width || _d3dpp.BackBufferHeight < height)
		{
			D3DPRESENT_PARAMETERS d3dpp = _d3dpp;
			d3dpp.BackBufferWidth = width;
			d3dpp.BackBufferHeight = height;
			HRESULT hr = _pd3d9device->Reset(&d3dpp);
			MK_PROCESS_COM_ERROR(hr);
			_d3dpp = d3dpp;
		}

		return SetViewport(_viewport.X, _viewport.Y, width, height, _viewport.MinZ, _viewport.MaxZ);

Exit0:
		return FALSE;
	}

	BOOL CD3D9RenderDevice::CreateRenderInput(RenderInputElemDesc* desc, DWORD descNum, CRenderInput*& renderInput)
	{
		BOOL ret = FALSE;
		CD3D9RenderInput* ri = new CD3D9RenderInput();
		MK_PROCESS_ERROR(ri);

		ret = ri->Create(desc, descNum);
		MK_PROCESS_ERROR(ret);

		renderInput = (CRenderInput*)ri;

		ret = TRUE;
Exit0:
		if (!ret)
			SAFE_DELETE(ri);

		return ret;
	}

	void CD3D9RenderDevice::DestroyRenderInput(CRenderInput*& pRenderInput)
	{
		if (!pRenderInput)
			return;

		CD3D9RenderInput* ri = (CD3D9RenderInput*)pRenderInput;
		delete ri;
		pRenderInput = NULL;
	}

	BOOL CD3D9RenderDevice::CreateVertexHardwareBuffer(DWORD vertexSize, DWORD vertexNum, HARDWARE_BUFFER_USAGE usage, CHardwareBuffer*& buffer)
	{
		BOOL ret = FALSE;
		CD3D9VertexHardwareBuffer* hb = new CD3D9VertexHardwareBuffer();
		MK_PROCESS_ERROR(hb);

		ret = hb->Create(vertexSize, vertexNum, usage);
		MK_PROCESS_ERROR(ret);

		buffer = (CHardwareBuffer*)hb;

		ret = TRUE;
Exit0:
		if (!ret)
			SAFE_DELETE(hb);

		return ret;
	}

	void CD3D9RenderDevice::DestroyVertexHardwareBuffer(CHardwareBuffer*& pBuffer)
	{
		if (!pBuffer)
			return;

		CD3D9VertexHardwareBuffer* hb = (CD3D9VertexHardwareBuffer*)pBuffer;
		delete hb;
		pBuffer = NULL;
	}

	BOOL CD3D9RenderDevice::CreateIndexHardwareBuffer(DWORD indexNum, INDEX_TYPE indexType, HARDWARE_BUFFER_USAGE usage, CHardwareBuffer*& buffer)
	{
		BOOL ret = FALSE;
		CD3D9IndexHardwareBuffer* ib = new CD3D9IndexHardwareBuffer();
		MK_PROCESS_ERROR(ib);

		ret = ib->Create(indexNum, indexType, usage);
		MK_PROCESS_ERROR(ret);

		buffer = (CHardwareBuffer*)ib;

		ret = TRUE;
Exit0:
		if (!ret)
			SAFE_DELETE(ib);

		return ret;
	}

	void CD3D9RenderDevice::DestroyIndexHardwareBuffer(CHardwareBuffer*& pBuffer)
	{
		if (!pBuffer)
			return;

		CD3D9IndexHardwareBuffer* ib = (CD3D9IndexHardwareBuffer*)pBuffer;
		delete ib;
		pBuffer = NULL;
	}

	BOOL CD3D9RenderDevice::BindRenderInput(CRenderInput* renderInput)
	{
		BOOL ret = FALSE;
		CD3D9RenderInput* pRI = (CD3D9RenderInput*)renderInput;
		LPDIRECT3DVERTEXDECLARATION9 pvd = pRI->GetD3D9VertexDeclaration();
		MK_PROCESS_ERROR(pvd);

		HRESULT hr = _pd3d9device->SetVertexDeclaration(pvd);
		MK_PROCESS_COM_ERROR(hr);

		ret = pRI->BindVertexBuffer(_pd3d9device);
		MK_PROCESS_ERROR(ret);

		ret = pRI->BindIndexBuffer(_pd3d9device, _useIndexBuffer);
		MK_PROCESS_ERROR(ret);

		ret = TRUE;
Exit0:
		return ret;
	}

	BOOL CD3D9RenderDevice::DrawPrimitive(PRIMITIVE_TYPE primitiveType, DWORD firstVertex, DWORD minIndex, DWORD vertexNum,
		DWORD firstIndex, DWORD primitiveNum)
	{
		D3DPRIMITIVETYPE pt = D3DPT_TRIANGLELIST;
		switch (primitiveType)
		{
		case PT_TRIANGLELIST:
			pt = D3DPT_TRIANGLELIST;
			break;

		case PT_TRIANGLESTRIP:
			pt = D3DPT_TRIANGLESTRIP;
			break;

		case PT_LINELIST:
			pt = D3DPT_LINELIST;
			break;

		case PT_LINESTRIP:
			pt = D3DPT_LINESTRIP;
			break;

		default:
			ASSERT(0);
			return FALSE;
		};

		HRESULT hr = E_FAIL;
		if (_useIndexBuffer)
			hr = _pd3d9device->DrawIndexedPrimitive(pt, firstVertex, minIndex, vertexNum, firstIndex, primitiveNum);
		else
			hr = _pd3d9device->DrawPrimitive(pt, firstVertex, primitiveNum);

		MK_PROCESS_COM_ERROR(hr);

#if MK_PROFILER_ENABLE
		GetEngine()->GetProfiler()->AddPrimitiveDrawCount(primitiveNum);
		GetEngine()->GetProfiler()->AddDrawCallCount(1);
#endif

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CD3D9RenderDevice::CreateTexture2DFromFile(const char* fileName, CTexture2D*& pTexture)
	{
		CD3D9Texture2D* texture = new CD3D9Texture2D(fileName);
		BOOL ret = texture->Load();
		MK_PROCESS_ERROR(ret);

		pTexture = (CTexture2D*)texture;
		return TRUE;
Exit0:
		SAFE_DELETE(texture);
		return FALSE;
	}

	BOOL CD3D9RenderDevice::CreateTexture2D(DWORD width, DWORD height, PIXEL_FORMAT format, 
		DWORD mip, TEXTURE_USAGE usage, CTexture2D*& pTexture)
	{
		CD3D9Texture2D* texture = new CD3D9Texture2D();
		MK_PROCESS_ERROR(texture);

		BOOL ret = texture->Create(width, height, format, mip, usage);
		MK_PROCESS_ERROR(ret);

		pTexture = (CTexture2D*)texture;
		return TRUE;
Exit0:
		SAFE_DELETE(texture);
		return FALSE;

		return TRUE;
	}

	BOOL CD3D9RenderDevice::CreateTexture2DFromMemory(const BYTE* buffer, DWORD size, CTexture2D*& pTexture)
	{
		CD3D9Texture2D* texture = new CD3D9Texture2D();
		BOOL ret = texture->LoadFromBuffer(buffer, size);
		MK_PROCESS_ERROR(ret);

		pTexture = (CTexture2D*)texture;
		return TRUE;
Exit0:
		SAFE_DELETE(texture);
		return FALSE;
	}

	void CD3D9RenderDevice::DestroyTexture2D(CTexture2D*& pTexture2D)
	{
		if (!pTexture2D)
			return;

		CD3D9Texture2D* tex = (CD3D9Texture2D*)pTexture2D;
		SAFE_DELETE(tex);
		pTexture2D = NULL;
	}

	BOOL CD3D9RenderDevice::CreateTexture3DFromFile(const char* fileName, CTexture3D*& pTexture)
	{
		CD3D9Texture3D* texture = new CD3D9Texture3D(fileName);
		BOOL ret = texture->Load();
		MK_PROCESS_ERROR(ret);

		pTexture = (CTexture3D*)texture;
		return TRUE;
Exit0:
		SAFE_DELETE(texture);
		return FALSE;
	}

	void CD3D9RenderDevice::DestroyTexture3D(CTexture3D*& pTexture)
	{
		if (!pTexture)
			return;

		CD3D9Texture3D* tex = (CD3D9Texture3D*)pTexture;
		SAFE_DELETE(tex);
		pTexture = NULL;
	}

	BOOL CD3D9RenderDevice::SetTexture(DWORD stage, CTexture2D* pTexture)
	{
		LPDIRECT3DTEXTURE9 d3dTex = NULL;
		if (pTexture)
		{
			CD3D9Texture2D* tex = (CD3D9Texture2D*)pTexture;
			d3dTex = tex->GetD3D9Texture();
		}

		HRESULT hr = _pd3d9device->SetTexture(0, d3dTex);
		MK_PROCESS_COM_ERROR(hr);

		return TRUE;
Exit0:
		return FALSE;
	}

	BOOL CD3D9RenderDevice::CreateShaderFromFile(const char* fileName, CShader*& shader)
	{
		BOOL ret = FALSE;
		CD3D9Shader* pShader = new CD3D9Shader(fileName);
		MK_PROCESS_ERROR(pShader);

		ret = pShader->Load();
		MK_PROCESS_ERROR(ret);

		shader = (CShader*)pShader;

		return TRUE;
Exit0:
		SAFE_DELETE(pShader);

		return FALSE;
	}

	BOOL CD3D9RenderDevice::CreateShaderFromMemory(const BYTE* buffer, DWORD size, CShader*& shader)
	{
		BOOL ret = FALSE;
		CD3D9Shader* pShader = new CD3D9Shader();
		MK_PROCESS_ERROR(pShader);

		ret = pShader->LoadFromBuffer(buffer, size);
		MK_PROCESS_ERROR(ret);

		shader = (CShader*)pShader;

		return TRUE;
Exit0:
		SAFE_DELETE(pShader);

		return FALSE;
	}

	void CD3D9RenderDevice::DestroyShader(CShader*& pShader)
	{
		if (!pShader)
			return;

		CD3D9Shader* shader = (CD3D9Shader*)pShader;
		delete shader;
		pShader = NULL;
	}

	BOOL CD3D9RenderDevice::CreateRenderTarget(DWORD width, DWORD height, PIXEL_FORMAT format, BOOL bDSBuffer, CRenderTarget*& pRenderTarget)
	{
		CD3D9RenderTarget* rt = new CD3D9RenderTarget();
		BOOL ret = rt->_Create(width, height, format, bDSBuffer);
		MK_PROCESS_ERROR(ret);

		pRenderTarget = (CRenderTarget*)rt;

		ret = TRUE;
Exit0:
		return ret;
	}

	void CD3D9RenderDevice::DestroyRenderTarget(CRenderTarget*& pRenderTarget)
	{
		if (!pRenderTarget)
			return;

		CD3D9RenderTarget* rt = (CD3D9RenderTarget*)pRenderTarget;
		delete rt;
		pRenderTarget = NULL;
	}

	BOOL CD3D9RenderDevice::SetRenderTarget(CRenderTarget* pRenderTarget)
	{
		BOOL ret = FALSE;
		HRESULT result = E_FAIL;

		// Reset to default render target.
		if (pRenderTarget == NULL)
		{
			if (_prevRenderTarget == NULL)
				return TRUE;

			result = _pd3d9device->SetRenderTarget(0, _prevRenderTarget);
			MK_PROCESS_COM_ERROR_NO_GOTO(result);

			if (_prevDSSurface)
			{
				result = _pd3d9device->SetDepthStencilSurface(_prevDSSurface);
				MK_PROCESS_COM_ERROR_NO_GOTO(result);
			}

			result = _pd3d9device->SetViewport(&_prevViewport);
			MK_PROCESS_COM_ERROR_NO_GOTO(result);

			SAFE_RELEASE(_prevRenderTarget);
			SAFE_RELEASE(_prevDSSurface);
		}
		// Set to new render target.
		else
		{
			if (_prevRenderTarget == NULL)
			{
				result = _pd3d9device->GetRenderTarget(0, &_prevRenderTarget);
				MK_PROCESS_COM_ERROR_NO_GOTO(result);

				if (pRenderTarget->HasIndependantDSBuffer())
				{
					result = _pd3d9device->GetDepthStencilSurface(&_prevDSSurface);
					MK_PROCESS_COM_ERROR_NO_GOTO(result);
				}

				result = _pd3d9device->GetViewport(&_prevViewport);
				MK_PROCESS_COM_ERROR_NO_GOTO(result);
			}

			ret = ((CD3D9RenderTarget*)pRenderTarget)->_SetRenderTarget(_pd3d9device);
			MK_PROCESS_ERROR_NO_GOTO(ret);
		}

		return TRUE;
	}

	BOOL CD3D9RenderDevice::SetRenderState(RENDER_STATE state, DWORD value)
	{
		D3DRENDERSTATETYPE d3dRenderState = RenderStateMapping(state);
		value = RenderStateValueMapping(state, value);
		return _pd3d9device->SetRenderState(d3dRenderState, value) == S_OK ? TRUE : FALSE;
	}

	DWORD CD3D9RenderDevice::GetMaxTextureSize()
	{
		return min(_d3dcaps.MaxTextureWidth, _d3dcaps.MaxTextureHeight);
	}

	BOOL CD3D9RenderDevice::SetClipPlane(DWORD index, D3DXPLANE& plane, BOOL enable)
	{
		HRESULT result = _pd3d9device->SetClipPlane(index, (float*)&plane);
		if (FAILED(result))
			return FALSE;

		return EnableClipPlane(index, enable);
	}

	BOOL CD3D9RenderDevice::EnableClipPlane(DWORD index, BOOL enable)
	{
		DWORD renderstate;
		_pd3d9device->GetRenderState(D3DRS_CLIPPLANEENABLE, &renderstate);
		if (enable)
			renderstate |= (1 << index);
		else
			renderstate &= ~(1 << index);
		_pd3d9device->SetRenderState(D3DRS_CLIPPLANEENABLE, renderstate);
		return TRUE;
	}
}